package cn.bdqn.interceptor;

import cn.bdqn.annotation.DataPermission;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import cn.bdqn.util.PermissionUtils;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.Properties;

/**
 * @author zhouxp
 * @create 2019-08-31 10:46
 */
//拦截StatementHandler类中参数类型为Statement的 prepare 方法
//即拦截 Statement prepare(Connection var1, Integer var2) 方法
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
@Component
@ConditionalOnProperty(value = "plugin.PrepareInterceptor.enabled", havingValue = "true")
public class PrepareInterceptor implements Interceptor {


	@Override
	public Object intercept(Invocation invocation) throws Throwable {

		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		//通过MetaObject优雅访问对象的属性，这里是访问statementHandler的属性;：MetaObject是Mybatis提供的一个用于方便、
		//优雅访问对象属性的对象，通过它可以简化代码、不需要try/catch各种reflect异常，同时它支持对JavaBean、Collection、Map三种类型对象的操作。
		MetaObject metaObject = MetaObject
				.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
						new DefaultReflectorFactory());
		//先拦截到RoutingStatementHandler，里面有个StatementHandler类型的delegate变量，其实现类是BaseStatementHandler，然后就到BaseStatementHandler的成员变量mappedStatement
		MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");


		//id为执行的mapper方法的全路径名，如com.uv.dao.UserMapper.insertUser
		//String id = mappedStatement.getId();
		//sql语句类型 select、delete、insert、update
		//String sqlCommandType = mappedStatement.getSqlCommandType().toString();
		//数据库连接信息
//        Configuration configuration = mappedStatement.getConfiguration();
//        ComboPooledDataSource dataSource = (ComboPooledDataSource)configuration.getEnvironment().getDataSource();
//        dataSource.getJdbcUrl();
		DataPermission dataPermission = PermissionUtils.getDataPermission(mappedStatement);
		if(dataPermission==null){
			//数据权限放行...
			return  invocation.proceed();
		}
		BoundSql boundSql = statementHandler.getBoundSql();
		//获取到原始sql语句
		String sql = boundSql.getSql();
		//System.out.println(sql);

		sql=permissionSql(sql,dataPermission);
		System.out.println(sql);
		//String mSql = sql + " limit 2";
		//通过反射修改sql语句
		Field field = boundSql.getClass().getDeclaredField("sql");
		field.setAccessible(true);
		field.set(boundSql, sql);

		return invocation.proceed();
	}
	private String permissionSql(String sql,DataPermission dataPermission) {
		StringBuilder sbSql = new StringBuilder(sql);
		StringBuilder sbcon =new StringBuilder();
		sbcon.append(" isdel=");
		sbcon.append(dataPermission.isdel());
		sbcon.append(" and isstop =");
		sbcon.append(dataPermission.isstop());
		if (!dataPermission.isadmin()){
			sbcon.append(" and isadmin =");
			sbcon.append(dataPermission.isadmin());
			sbcon.append(" ");
		}
		int limit = sbSql.lastIndexOf("LIMIT");
		int count = sbSql.lastIndexOf("count(0)");
		if(limit>1){
			if (sbSql.charAt(limit-2)==')'){
				sbcon.insert(0," and ");
				sbSql.insert(limit-1, sbcon.toString());
			}else {
				sbcon.insert(0, " where ");
				sbSql.insert(limit, sbcon.toString());
			}
		}else if(count>1) {
			if(sbSql.charAt(sbSql.length()-1)==')'){
				sbcon.insert(0," and ");
				sbSql.insert(sbSql.length()-1, sbcon.toString());
			}else {
				sbcon.insert(0, " where ");
				sbSql.insert(sbSql.length(), sbcon.toString());
			}
		}else{
				sbSql.insert(0, "select * from (");
				sbSql.append(" ) s where ");
				sbSql.append(sbcon.toString());

		}
		return sbSql.toString();
	}
	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {
		//此处可以接收到配置文件的property参数
		System.out.println(properties.getProperty("name"));
	}

}

